﻿module MaybeMonad

type MaybeOrFail<'a> = 
  | Just of 'a 
  | Fail of string
  
type MaybeBuilder() =
  member this.Return(x:'a) = 
    fun s -> Just (x,s)
  member this.Bind(p, k )  =
    fun s -> 
      match p s with
      Fail s -> Fail s
      | Just (x, s') -> (k x) s'
  member this.Combine(p, k )  =
    this.Bind(p, fun _ -> k)

  member this.For(items:seq<'a>, body) =
    fun s ->
      if items |> Seq.length = 0 then 
        this.Return() s
      else
        let hd,tl = items |> Seq.head, items |> Seq.skip 1
        this.Combine(body hd, this.For(tl, body)) s

  //membri interni necessari per la gestione e la creazione della monade
  member this.Delay s = this.Bind(this.Return(), s)
  member this.Run s = s
  member this.Zero () = fun s -> None
  member this.ReturnFrom p = p  

let setMaybeState s = (fun _ -> (), s)  

let execute m s = m s //|> snd 
